面向对象有三个特点,一个个来说:
封装
私有变量
利用闭包实现对象的私有变量。
function Animal (age) {
this.getAge = function () {
return age
}
}
var dog = new Animal(3)
console.log(dog.age) // undefined
console.log(dog.getAge()) // 3
碰过的一个有趣的问题:
实现一个book构造函数,有一个属性id,每次调用该值加1。
运用闭包和立刻执行函数。
let Book = (function () {
let id = 1
return function () {
this.id = id++
}
})()
let bok1 = new Book()
let bok2 = new Book()
let bok3 = new Book()
console.log(bok3.id) // 3
共有方法
看下面的代码可以发现,getAge被重复创建了
var dog = new Animal(3)
var cat = new Animal(5)
console.log(dog.getAge === dog.getAge) // false
如果不想方法或者属性在每次new时新创建一份,可以将其设置在构造函数的原型prototype上。
Animal.prototype.feed = function () {
console.log('feed')
}
console.log(dog.feed === cat.feed) // true
继承
听说继承有六种方法,假设让Dog继承Animal,无非就是for in 复制属性,修改原型链如dog.prototype = new Animal,直接Object.create,在Dog中使用Animal.call
然而我们记住最好的一种就够了,就是组合继承。
先试下这样写,利用call的继承:
function Animal (name) {
this.name = name;
this.say = function() {
console.log(this.name)
}
}
// *1
function Dog (color, name) {
Animal.call(this, name)
this.color = color
}
// *2
let wangcai = new Dog('blue', 'wangcai')
console.log(wangcai) // {color: 'blue', name: 'wangcai')
wangcai.say() // 'wangcal'
可是如果在*1处加上这样的代码
Animal.prototype.say2 = function() {
console.log(this.name)
}
在*3处输入
wangcai.say2(),会报错提示不存在该方法,说明我们的继承是不完整的。dog没有继承原型链上的方法
我们需要在*2补上:
Dog.prototype = Object.create(Animal.prototype)
这时候不会报错了,补上Object.create的polyfill
function objectCreate (proto) {
function F() {}
F.prototype = proto;
return new F();
}
然而还有一点小漏洞,当我们查看wangcai.constructor时,会发现指向的是Animal。因此我们需要修复一下:
Dog.prototype.constructor = Dog
补充一下new的模拟
function fNew (base) {
var o = {}
o.__proto__ = base.prototype
base.call(o)
return o
}
完整代码
function Animal (name) {
this.name = name;
this.say = function() {
console.log(this.name)
}
}
Animal.prototype.say2 = function () {
console.log(this.name)
}
function Dog (color, name) {
Animal.call(this, name)
this.color = color
}
Dog.prototype = Object.create(Animal.prototype)
Dog.prototype.constructor = Dog
let wangcai = new Dog('blue', 'wangcai')
检测继承是否成功的代码:
console.log(wangcai instanceof Animal)
console.log(wangcai instanceof Dog)
console.log(wangcai.constructor === Dog)
console.log(wangcai.say2 === Animal.prototype.say2)
console.log(wangcai.__proto__ === Dog.prototype)
console.log(wangcai.__proto__.__proto__ === Animal.prototype)
console.log(wangcai.__proto__.__proto__.__proto__ === Object.prototype)
console.log(wangcai.constructor === Dog)
使用Object.create()和修复Dog.prototype.constructor = Dog是不是挺多余的?es6提供了这么一个函数Object.setPrototypeOf.
因此我们可以使用
Object.setPrototypeOf(Dog.prototype, Animal.prototype)
替换刚刚提到的两行代码
可以了解到Object.setPrototypeOf(A,B)相当于令A.__proto__ = B。
多态
一个函数可以应用于不同的对象。并且根据this的不同,函数调用的结果也不同
function test() {
alert([this.a, this.b]);
}
test.call({a: 10, b: 20}); // 10, 20
test.call({a: 100, b: 200}); // 100, 200
var a = 1;
var b = 2;
test(); // 1, 2
或是在函数中检测arguments的数量和类型来实现多态
function add (a, b) {
if (arguments.length === 2) {
return a + b
} else {
return a + 1
}
}
console.log(add(1,4))
console.log(add(1))
END
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。